#include "netdisk.h"

int login(int ntfd, char *uname)
{
    int uerror = 2;  // 用于表示用户名错误的返回值
    int perror1 = 1; // 用于表示密码错误的返回值
    int right = 0;   // 用于表示验证通过的返回值
    char username[256] = {0}; // 用于存储客户端发送的用户名
    char password[256] = {0}; // 用于存储客户端发送的密码
    char salt_value[128];     // 用于存储密码的盐值
    char MD5_pd_local[256];   // 用于存储从系统中获取的加密密码
    char *MD5_pd_getting;     // 用于存储客户端输入密码的加密结果
    int times = 4;            // 允许用户尝试登录的次数

    // 用于存储用户名和密码的长度
    int name_length = 0; 
    int pswd_length = 0;
    /**************************************************************/
    // 用户名 times--;
    /**************************************************************/
    // 循环接收用户名和密码
    while (1)
    {
        // 清空 username 和 password 数组
        bzero(username, sizeof(username));
        bzero(password, sizeof(password));
        // 检查是否还有尝试次数，如果用户登录次数只有0次，则返回 -1 表示登陆失败！
        if (times == 0) // 多次密码错误退出
        {
            return -1;
        }

        // 使用 recvn 函数接收客户端发送的用户名长度、用户名、密码长度和密码（“小火车”协议）
        recvn(ntfd, &name_length, sizeof(int));
        recvn(ntfd, username, name_length);
        recvn(ntfd, &pswd_length, sizeof(int));
        recvn(ntfd, password, pswd_length);

        printf("输入的用户名为：%s\n", username);
        printf("输入的密码为：%s\n", password);


        // 验证用户是否存在
        struct spwd *pswd;
        // 调用 getspnam 函数，根据用户名获取用户的加密密码信息
        /* getspnam 函数的作用是根据用户名从系统的密码数据库中检索用的加密密码信息
         *      密码数据库通常存储在 /etc/shadow 文件中，该文件包含用户密码的加密版本以及其它与密码i相关的安全信息
         *  返回值：
         *      如果成功找到指定用户名的密码信息，getspnam 返回一个指向struct spwd 结构的指针
         *      如果未找到指定用户名的密码信息，或者发生错误，则返回 NULL
         * */ 
        pswd = getspnam(username);
        // 若是 getspnam 返回 NULL，表示用户名不存在，发送错误码 uerror 给客户端，并继续下一次u想你换
        if (pswd == NULL)
        {
            send(ntfd, &uerror, sizeof(int), 0);
            continue;
        }
        // 若是用户名存在，则将用户名复制到 uname
        strcpy(uname, username);
        /**************************************************************/
        // 密码
        /**************************************************************/

        // 验证密码是否正确
        //-获取盐值

        // 将从系统中获取的加密密码存储到 MD5_pd_local 中
        // ！！！！！！sp_pwdp：加密的密码字符串！！！！！！
        strcpy(MD5_pd_local, pswd->sp_pwdp);
        // 从加密密码中提取盐值，加密密码通常以 $ 分割，盐值位于两个 $ 之间
        int first_money, last_money;
        bool first_bool = 0;
        for (int i = 0; i < strlen(MD5_pd_local); i++)
        {
            if (MD5_pd_local[i] == '$' && first_bool == 0)
            {
                first_money = i;
                first_bool = 1;
            }
            if (MD5_pd_local[i] == '$' && first_bool == 1)
            {
                last_money = i;
            }
        }

        // 盐值是密码加密过程中使用的一个随机字符串，用于增加密码的安全性。
        //  在验证密码时，需要使用相同的盐值对输入的密码进行加密，然后与存储的加密密码进行比较

        // 将提取到的盐值存储到 salt_value 中i
        for (int i = first_money; i <= last_money; i++)
        {
            salt_value[i - first_money] = MD5_pd_local[i];
        }
        //-验证密码
        // 使用 crypt 函数使用盐值对客户端输入的密码进行加密，随后返回一个加密后的字符串
        MD5_pd_getting = crypt(password, salt_value);
        // 比较加密后的密码与系统中存储的密码是否一致
        if (strcmp(MD5_pd_getting, MD5_pd_local) == 0) // 密码正确
        {
            // 若是密码一致，则发送 right 给客户端，并表示 0 表示登录成功
            send(ntfd, &right, sizeof(int), 0);
            return 0;
        }
        else // 密码错误
        {
            // 若是密码错误，则发送 perror1 给客户端，减少尝试次数 times，并继续下一次循环 
            send(ntfd, &perror1, sizeof(int), 0);
            times--;
            continue;
        }
    }
    return -1;
}
